#!/bin/bash
#       Name: searchsploit - Exploit-DB's CLI search tool
#    Version: 3.1  (Release date: 2015-07-08)
# Written by: Offensive Security, Unix-Ninja & g0tmi1k
#   Homepage: https://github.com/offensive-security/exploit-database

## NOTE:
#   Exit code '0' means finished normally
#   Exit code '1' means finished help screen
#   Exit code '6' means updated from GitHub


## OS settings
gitpath="/usr/share/exploitdb"
csvpath="${gitpath}/files.csv"

## Program settings
gitremote="https://github.com/offensive-security/exploit-database.git"
progname="$( basename "$0" )"

## Default options
TAGS=""
SCASE="tolower"
VERBOSE=0
WEBLINK=0
EDBID=0
COLOUR='true'
FILEPATH=1


## If files.csv is in the searchsploit path, use that instead
if [[ -f "$( dirname "$0" )/files.csv" ]]; then
  csvpath="$( dirname "$0" )/files.csv"
fi


## Usage info
function usage()
{
  echo "  Usage: ${progname} [options] term1 [term2] ... [termN]"
  echo "Example:"
  echo "  ${progname} afd windows local"
  echo "  ${progname} -t oracle windows"
  echo
  echo "========="
  echo " Options "
  echo "========="
  echo "   -c, --case     Perform a case-sensitive search (Default is insensitive)."
  echo "   -h, --help     Show this help screen."
  echo "   -t, --title    Search just the exploit title (Default is title AND the file's path)."
  echo "   -u, --update   Update exploit database from git."
  echo "   -v, --verbose  Verbose output. Title lines are allowed to overflow their columns."
  echo "   -w, --www      Show URLs to Exploit-DB.com rather than local path."
  echo "       --colour   Disable colour highlighting."
  echo "       --id       Display EDB-ID value rather than local path."
  echo
  echo "======="
  echo " Notes "
  echo "======="
  echo " * Use any number of search terms, in any order."
  echo " * Search terms are not case sensitive, and order is irrelevant."
  echo "   * Use '-c' if you wish to reduce results by case-sensitive searching."
  echo "* Use '-t' to exclude the file's path to filter the search results."
  echo "   * Could possibly remove false positives (especially when searching numbers)."
  echo " * When updating from git or displaying help, search terms will be ignored."
  echo ""
  exit 1
}

## Update database (via GIT)
function update()
{
  cd "${gitpath}/"

  # Make sure a git repo is init before updating
  if [[ "$( git rev-parse --is-inside-work-tree )" != "true" ]]; then
    if [[ "$( ls )" = "" ]]; then
      # If directory is empty, just clone
      git clone "${gitremote}" .
    else
      # If not empty, init and add remote
      git init >/dev/null
      git remote add origin "${gitremote}"
    fi
  fi

  # Make sure to prep checkout first
  git checkout -- .

  # Update from git
  git pull origin master

  # If conflicts, clean and try again
  if [[ "$?" -ne 0 ]]; then
    git clean -d -fx ""
    git pull origin master
  fi

  echo "[*] Update finished."
  exit 6
}


## Printing dotted lines in the correct manner
function drawline()
{
  printf "%0.s-" $( eval echo {1..$(( COL1 + 1 ))} )
  echo -n " "
  printf "%0.s-" $( eval echo {1..$(( COL2 - 1 ))} )
  echo ""
}


## Check for empty args
if [[ $# -eq 0 ]]; then
  usage >&2
fi


## Parse long arguments
ARGS="-"
for param in "$@"; do
  if [[ "${param}" == "--case" ]]; then
    SCASE=''
  elif [[ "${param}" == "--help" ]]; then
    usage >&2
  elif [[ "${param}" == "--title" ]]; then
    FILEPATH=0
  elif [[ "${param}" == "--update" ]]; then
    update
  elif [[ "${param}" == "--www" ]]; then
    WEBLINK=1
  elif [[ "${param}" == "--verbose" ]]; then
    VERBOSE=1
  elif [[ "${param}" == "--colour" ]] || [[ "${param}" == "--color" ]]; then
    COLOUR=''
  elif [[ "${param}" == "--id" ]]; then
    EDBID=1
  else
    if [[ "${param:0:1}" == "-" ]]; then
      ARGS=${ARGS}${param:1}
      shift
      continue
    fi
    TAGS="${TAGS} ${param}"
  fi
done


## Parse short arguments
while getopts "chtuvw" arg "${ARGS}"; do
  if [[ "${arg}" = "?" ]]; then
    usage >&2;
  fi
  case ${arg} in
    c) SCASE='';;
    h) usage >&2;;
    t) FILEPATH=0;;
    u) update;;
    v) VERBOSE=1;;
    w) WEBLINK=1;;
  esac
  shift $(( OPTIND - 1 ))
done


## Dynamically set column widths
if [[ "${WEBLINK}" -eq '1' ]]; then
  COL2=45
else
  COL2=35
fi
COL1=$(( $( tput cols ) - COL2 - 1 ))


## Print header
drawline
printf "%-${COL1}s %s" " Exploit Title"
if [[ "${WEBLINK}" -eq '1' ]]; then
  echo "|  URL"
elif [[ "${EDBID}" -eq '1' ]]; then
  echo "|  EDB-ID"
else
  echo "|  Path"
  printf "%-${COL1}s "
  echo "| (${gitpath}/platforms)"
fi
drawline

## Create (AND) search command
SEARCH=
for tag in ${TAGS}; do

  if [[ "${COLOUR}" ]]; then
    COLOUR="${COLOUR}\|${tag}"
  fi

  if [[ "${FILEPATH}" -eq 1 ]]; then
    if [[ "${SCASE}" ]]; then
      SCASE='-i'
    fi

    if [[ "${SEARCH}" ]]; then
        SEARCH="${SEARCH} |"
    fi

    SEARCH="${SEARCH} fgrep ${SCASE} \"${tag}\""
  else
    if [[ "${SEARCH}" ]]; then
      SEARCH="${SEARCH}/ && ${SCASE}(\$1) ~ /"
    fi

    if [[ "${SCASE}" ]]; then
      tag="$( echo ${tag} | tr '[:upper:]' '[:lower:]' )"
    fi

    SEARCH="${SEARCH}${tag}"
  fi
done

if [[ "${FILEPATH}" -ne 1 ]]; then
  SEARCH="awk -F '[|]' '${SCASE}(\$1) ~ /${SEARCH}/ {print}'"
fi

if [[ "${COLOUR}" ]]; then
  SEARCH="${SEARCH} | grep --color=always -ie \"\${COLOUR}\""
fi


## Set LANG variable to avoid illegal byte sequence errors
LANG=C


## Search, format, and print results
if [[ "${VERBOSE}" -eq 0 ]]; then
  FORMAT=${COL1}'.'${COL1}
else
  FORMAT=${COL1}
fi


## Web link format?
if [[ "${WEBLINK}" -eq '1' ]]; then
  ## Magic search Fu
  awk -F "\"*,\"*" '{ printf "%-'${FORMAT}'s | %s\n", $3, "https://www.exploit-db.com/exploits/"$1"/"}' "${csvpath}" \
    | eval "${SEARCH}"
elif [[ "${EDBID}" -eq '1' ]]; then
  ## Magic search Fu
  awk -F "\"*,\"*" '{ printf "%-'${FORMAT}'s | %s\n", $3, $1}' "${csvpath}" \
    | eval "${SEARCH}"
else
  ## Magic search Fu
  awk -F "\"*,\"*" '{ printf "%-'${FORMAT}'s | %s\n", $3, $2}' "${csvpath}" \
    | eval "${SEARCH}" \
    | sed "s/| platforms/| ./"
fi


## Print footer
drawline


## Done
exit 0
